/*
--------------------------------------------------------------------------------
- 文件：area.c

- 模块：ar

- 作用：管理大数据块，数据块根据需求自动分配。
        新分配的内存块的连接自动完成。
        
- 记录：2004.11.23 建立，version 0.1 ：王明东
 
--------------------------------------------------------------------------------
*/


#include <stdlib.h>
#include <assert.h>
#include "area.h"


/*
// 每个区由若干个内存块组成，每个块是 4096 个字节。
// 每个块的第 0 个整数，指向下个区。
// 所以是单链表结构。
// 所以每个区只有 4092 个字节存放真正的数据。
*/
#define  SPEC_AREA_ID  (0xFFFFFFFF)
#define  DATA_SIZE    (4092)

/* 内存块结构 */ 
typedef  struct _sub_block
{
    struct _sub_block * next;
    byte  data[DATA_SIZE];
} sub_block;


/* 区管理结构，区管理结构。  */
typedef  struct
{
    tetra  element;          // 固定尺寸的。
    tetra  length;           // 当前总长度。
    sub_block *  head;       // 开始块的地址。
    sub_block *  tail;       // 最后块的地址。
} area_info;


/*
// 这是 area 0 的第一个内存块，area 0 中存放的是所以区的管理结构，包块它自己的。
// 所以 area 0 不是用 area_create 创建的，它是下面的这个静态全局变量.
// 这有点鸡生蛋，蛋生鸡的味道。
*/
static tetra root[1024] = 
{
    0,
    16, 16, (tetra)&root, (tetra)&root
};

/*
////////////////////////////////////////////////////////////////////////////////
// 功能：
//   创建一存储区，以后就可以使用返回的 id 值来使用区管理数据。
// 
// 输入：
//   ele_size : 单元尺寸，当区中存放的是等大小的相同结构时使用。

// 返回：
//       -1 : 不成功. 
//    other : 成功。
////////////////////////////////////////////////////////////////////////////////
*/
tetra  area_create( tetra  ele_size )
{
    sub_block *  pblock;
    area_info  tempinfo, tempinfo2;
    tetra  i, j, k;
    
    // 判断非法参数。
    if( ele_size <= 0 )
    {
        return AREA_INVALID_ID;
    }

    // 先为这个区分配一个内存块。
    pblock = (sub_block *) malloc( sizeof(sub_block) );
    if( pblock == NULL )
    {
        return AREA_INVALID_ID;
    }
    
    pblock->next = NULL;
    tempinfo.element = ele_size;        // 元素尺寸。
    tempinfo.length = 0;                // 当前存储的数据长度。
    tempinfo.head = pblock;             // 数据块构成单链的头指针。
    tempinfo.tail = pblock;             // 数据块构成单链的尾指针。
    
    // 取得 area 0 的当前数据长度。 
    j = area_add_data( SPEC_AREA_ID, 0, NULL );
    
    // 查找是否有空闲的区管理结构。
    // 注意跳过了第一个 area_info 结构，因为这个是描述自己的。
    for( i = sizeof(area_info); i<j; i += sizeof(area_info) )
    {
        area_read_data( SPEC_AREA_ID, i, sizeof(area_info), &tempinfo2 );
        if( tempinfo2.element == 0 )
        {
            break;
        }
    }
    
    
    if( i >= j )
    {
        // 没发现有空的。
        k = area_add_data( SPEC_AREA_ID, sizeof(area_info), &tempinfo );
        return( k/sizeof(area_info) );
    }
    else
    {
        // 在 i 位置有一个空的。
        k = area_write_data( SPEC_AREA_ID, i, sizeof(area_info), &tempinfo );
        assert( k == sizeof(area_info));
        return( i/sizeof(area_info) );
    }
}


/*
////////////////////////////////////////////////////////////////////////////////
// 功能：
//    删除这个区，释放资源，以后就不能使用这个 id 值引用这个块了。
// 输入：
//    id : 要删除的区的 id  值 。
// 返回：
//    无。
////////////////////////////////////////////////////////////////////////////////
*/
void  area_delete( tetra  id )
{
    sub_block * pblock, * pblock1;
    area_info  tempinfo;
    tetra  i;
    
    // 参数合法性。
    assert( id != 0 );
    assert( id != SPEC_AREA_ID );
    
    // 
    i = area_read_data( SPEC_AREA_ID, id*sizeof(area_info), sizeof(area_info), &tempinfo );
    if( i != sizeof(area_info) )
    {
        return;
    }
    
    // 释放块。
    pblock = tempinfo.head;
    while( pblock != NULL )
    {
        pblock1 = pblock->next;
        free( pblock );
        pblock = pblock1;
    }
    
    // 写管理信息为空。 
    tempinfo.element = 0;
    area_write_data( 0, id*sizeof(area_info), sizeof(area_info), &tempinfo );
    
    return;
}




/*
////////////////////////////////////////////////////////////////////////////////
// 功能：
//    向块中填加数据，在当前区中数据的末尾填加。
// 输入：
//    id :
//   len :
//   buf :
// 
// 返回:
//   返回 area 填加数据 qian ..的长度。
//   可以使用 len = 0 来调用本函数，来获得区的当前长度。
//   可是如果 len 不为 0 时，而返回值是 0 ，表示填加数据失败。
////////////////////////////////////////////////////////////////////////////////
*/
tetra  area_add_data( tetra id, tetra len, void * buf )
{
    area_info  tempinfo;
    tetra  i, j, k;
    
  
    // 根据 id 找到它的记录结构。
    // 得到它的 area_info 结构。
    assert( id != 0 );
    if( id == SPEC_AREA_ID )
    {
        memcpy( &tempinfo, ((sub_block *)root)->data, sizeof(area_info) );
        i = sizeof(area_info);
    }
    else
    {
        i = area_read_data( SPEC_AREA_ID, id*sizeof(area_info), sizeof(area_info), &tempinfo );
    }

    if( i != sizeof(area_info) )
    {
        // 可能 id 值太大，超过了范围。
        // 原因就是 id 值不是使用 area_create 返回的。
        return 0;
    }

    if( len == 0 )
    {
        // len 值为 0 ，用户只是想得到当前 area 的总长度。
        return tempinfo.length;
    }

    /* 检查数据指针的合法性。 */
    if( buf == NULL )
    {
        return 0;
    }

    /* 先保存，在返回时使用。 */
    k = tempinfo.length;
    
    // 写入数据。 
    for(;;)
    {
        i = tempinfo.length % DATA_SIZE;    // 本块中已写字节数。
        j = DATA_SIZE - i;                  // 还可写的字节数。
        
        if( j >= len )
        {
            // 本块能满足要求，写入数据后跳出循环。
            memcpy( tempinfo.tail->data + i, buf, len );
            tempinfo.length += len;
            break;
        }
        else
        {
            // 本块不能满足要求，先写满再说。
            memcpy( tempinfo.tail->data + i, buf, j );
            tempinfo.length += j;
            
            // 重新计数数据 buf 和 len 。
            buf = (byte *)buf + j;
            len -= j;
            
            // 分配新块。
            tempinfo.tail->next = malloc( sizeof(sub_block) );
            assert(tempinfo.tail->next != NULL);
            tempinfo.tail = tempinfo.tail->next;
            tempinfo.tail->next = NULL;
        }
    }

    
    // 更新区管理结构中的数据长度值。
    if( id == SPEC_AREA_ID )
    {
        memcpy( ((sub_block *)root)->data, &tempinfo, sizeof(area_info) );
    }
    else
    {
        area_write_data( SPEC_AREA_ID, id * sizeof(area_info), sizeof(area_info), &tempinfo );
    }
    return k;
}


/*
////////////////////////////////////////////////////////////////////////////////
// 功能：
//    从区中 ofs 偏移处读取数据到 buf 处的缓冲区中。
// 输入：
//    id : 函数操作的区 id  值。
//   ofs : 从那个位置开始读取数据。
//   len : 要读取数据的长度，以字节为单位。
//   buf : 读出数据的存放处。
// 返回：
//    返回实际读取的字节长度。
//    因为 ofs+len 有可能大于区中数据总长度，所以返回值有可能小于 len .
////////////////////////////////////////////////////////////////////////////////
*/
tetra  area_read_data( tetra  id, tetra ofs, tetra len, void * buf )
{
    sub_block *  pblock;
    area_info  tempinfo;
    tetra  i, j, k;
    tetra  templen;
    
    // 参数合法性。
    assert( id != 0 );
    
    // 得到它的 area_info 结构。
    if( id == SPEC_AREA_ID )
    {
        memcpy( &tempinfo, ((sub_block *)root)->data, sizeof(area_info) );
    }
    else
    {
        area_read_data( SPEC_AREA_ID, id*sizeof(area_info), sizeof(area_info), &tempinfo );
    }
    
    // 有了 tempinfo 结构，操作起来 area 0 和其它 area 都是一样的。
    
    // ofs 是否超过数据范围。
    if( ofs >= tempinfo.length )
    {
        return 0;
    }
    
    // 修正可读数据数。
    if( ofs + len > tempinfo.length )
    {
        len = tempinfo.length - ofs;
    }
    
    templen = len;                  // 返回值。
    pblock = tempinfo.head;
    for( k=DATA_SIZE; k<=ofs; k+=DATA_SIZE )
    {
        pblock = pblock->next;
    }
    
    // 找到开始位置。
    for(;;)
    {
        i = ofs % DATA_SIZE;    // 本块中的开始偏移。
        j = DATA_SIZE - i;      // 本块中可读数据，可能比实际大，不过没关系，为什么？
        
        if( len <= j )
        {
            // v..v..v
            memcpy( buf, pblock->data + i, len );
            break;
        }
        else
        {
            memcpy( buf, pblock->data + i, j );
            pblock = pblock->next;
            ofs += j;
            len -= j;
            buf = (byte *)buf + j;
        }
    }
    
    return templen;
}

/*
////////////////////////////////////////////////////////////////////////////////
// 功能：
//    修改块中 ofs 处 len 长度的数据内容。
// 输入：
//    id :
//   ofs :
//   len :
//   buf :
// 
// 返回：
//    如果在 ofs 处开始到区的结束并没有 len 长的内容可供修改。
//    则返回 0, 而且不对块的内容作任何修改。
//    否则返回 len ;
////////////////////////////////////////////////////////////////////////////////
*/
tetra  area_write_data( tetra  id, tetra ofs, tetra len, void * buf )
{
    sub_block * pblock;
    area_info  tempinfo;
    tetra  i, j, k;
    tetra  templen;
    
    // 参数合法性。
    if( id == 0 || ofs < 0 || len < 0 )
    {
        return 0;
    }
    
    // 得到它的 area_info 结构。
    if( id == SPEC_AREA_ID )
    {
        memcpy( &tempinfo, ((sub_block *)root)->data, sizeof(area_info) );
    }
    else
    {
        area_read_data( SPEC_AREA_ID, id*sizeof(area_info), sizeof(area_info), &tempinfo );
    }
    
    // 有了 tempinfo 结构，操作起来 area 0 和其它 area 都是一样的。

    // ofs 是否超过数据范围。
    if( ofs >= tempinfo.length )
    {
        return 0;
    }
    
    // 不允许写操作超过范围。
    if( ofs + len > tempinfo.length )
    {
        return 0;
    }
    
    templen = len;                  // 返回值。
    
    // 找到开始块。
    pblock = tempinfo.head;
    for( k=DATA_SIZE; k<=ofs; k+=DATA_SIZE )
    {
        pblock = pblock->next;
    }

    // 找到开始位置。
    for(;;)
    {
        i = ofs % DATA_SIZE;    // 本块中的开始偏移。
        j = DATA_SIZE - i;      // 本块中可读数据，可能比实际大，不过没关系，为什么？
        
        if( len <= j )
        {
            //
            memcpy( pblock->data + i, buf, len );
            break;
        }
        else
        {
            memcpy( pblock->data + i, buf, j );
            pblock = pblock->next;
            ofs += j;
            len -= j;
            buf = (byte *)buf + j;
        }
    }
    
    return templen;
}

/*
////////////////////////////////////////////////////////////////////////////////
// 功能：
//    搜索功能，认为 area 中存储的是 utf32 格式的字符串。
//    在指定的 ofs 处开始按 tetra 类型递进比较，直到遇到 0 结束。
//
// 输入：
//    id : 操作对 area 的 id 值。
//   ofs : 目的字符串在 area 中对位置。
//   str : 要搜索的源字符串。
// 
// 返回：
//    0 表示字符串相同。
//    否则不同。
//    
////////////////////////////////////////////////////////////////////////////////
*/
tetra  area_compare_str( tetra id, tetra ofs, tetra * str )
{
    tetra  tempt, tempt2 ;
    tetra  tempstr[260];
    
    tempt = utf32_strlen( str );
    assert( tempt != 0 );
    assert( tempt <= 256 );
    
    /**/
    tempt2 = area_read_data( id, ofs, (tempt+1)*sizeof(tetra), tempstr );
    if( tempt2 != (tempt+1)*sizeof(tetra) )
    {
         return 1;
    }
    else
    {
         return  memcmp( tempstr, str, (tempt+1)*sizeof(tetra) );
    }
}


/*
////////////////////////////////////////////////////////////////////////////////
// 功能：
//    存一个字符串进入 area 中。
//
// 输入：
//    id : 操作对 area 的 id 值。
//   str : 要被存进 area 的源字符串。
// 
// 返回：
//    返回的被加入的字符串的偏移量，其实就是字符被加入前 area 的长度。
//    
////////////////////////////////////////////////////////////////////////////////
*/
tetra  area_add_str( tetra id, tetra * str )
{
    tetra  len;
    tetra  tempt;
    
    assert( id != 0 );
    assert( str != NULL );
    len = utf32_strlen( str );
    assert( len != 0 );

    /**/
    tempt = area_add_data( id, (len+1)*sizeof(tetra), str );

    len = area_add_data( id, 0, NULL );
    assert( len > tempt );

    return tempt;
}


/*
////////////////////////////////////////////////////////////////////////////////
// 功能：
//    从 area 中读出一个字符串s。
//
// 输入：
//    id : 操作对 area 的 id 值。
//   ofs : 字符串在 area 中的字节位置偏移量。
//   str : 要被存进 area 的源字符串。
// 
// 返回：
//    返回字符串指针，其实就是提供的 str.
//    
////////////////////////////////////////////////////////////////////////////////
*/
tetra * area_read_str( tetra id, tetra ofs, tetra * str )
{
    assert( id != 0 );
    return NULL;
}


